﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace NodeBasedSkillEditor.Model
{
    public class TypeSpreadMap
    {
        public Type CurrentDataType;
        public HashSet<Type> AllowedTypes = new();
        public HashSet<ViewModel.VM_Port> ImmutableTypePorts = new();
        public HashSet<ViewModel.VM_Port> MutableTypePorts = new();
    }

    public class TypeSpreadMapLinkResult
    {
        public bool IsCompatible { get { return DataType != null; } }
        public Type DataType { get; private set; } = null;
        public HashSet<ViewModel.VM_Port> PortsToChangeType { get; private set; } = new();

        public TypeSpreadMapLinkResult(TypeSpreadMap map1, TypeSpreadMap map2)
        {

            /// 当Redo_CreateLink的时候，很可能同时恢复一个带类型的SubPort，然后把另一方的端口连上这个SubPort
            /// 由于类型相同，这将导致传导网络的检查被跳过，又因为无论主动操作或通过Undo/Redo断掉Link，端口类型都不会自动退化
            /// 这就给类型传导网络内的类型一致性带来了问题，所以必须严格的对每一次类型传导网络做完整的检查
            //// 类型相同时，没有任何端口需要改变类型
            //if (map1.CurrentDataType == map2.CurrentDataType)
            //{
            //    DataType = map1.CurrentDataType;
            //}
            var sameType = map1.CurrentDataType == map2.CurrentDataType;

            // 类型不同时，若双方都有不可变端口，那么必然不兼容
            if (!sameType && map1.ImmutableTypePorts.Count > 0 && map2.ImmutableTypePorts.Count > 0)
            {
                return;
            }
            else if (sameType)
            {
                DataType = map1.CurrentDataType;
                PortsToChangeType.UnionWith(map1.MutableTypePorts);
                PortsToChangeType.UnionWith(map2.MutableTypePorts);
            }
            else
            {
                // 经过上方的过滤，此处2个map至多只有1个是具有不可变类型端口的，且类型不同
                // 通过对AllowTypes求交集，足以保证ValidTypes数量大于0时，2个map必然存在可以兼容的类型
                // 此处在处理上优先保证map1的类型不变
                HashSet<Type> validTypes = new();
                validTypes.UnionWith(map1.AllowedTypes.Intersect(map2.AllowedTypes));

                // 先针对枚举的特殊情况进行处理
                if (map1.CurrentDataType.IsEnum)
                {
                    if (ProcessWhenMap1IsEnum(map1, map2, validTypes))
                    {
                        return;
                    }
                }
                else if (map2.CurrentDataType.IsEnum)
                {
                    if (ProcessWhenMap1IsEnum(map2, map1, validTypes))
                    {
                        return;
                    }
                }

                if (validTypes.Count > 0)
                {
                    // 双方都需要改变类型
                    if (!validTypes.Contains(map1.CurrentDataType) && !validTypes.Contains(map2.CurrentDataType))
                    {
                        // 倘若首选可用的类型是Enum，那么将会发生端口类型退化
                        DataType = validTypes.First();
                        if (DataType != null)
                        {
                            PortsToChangeType.UnionWith(map1.MutableTypePorts);
                            PortsToChangeType.UnionWith(map2.MutableTypePorts);
                        }
                    }
                    // map1需要改变类型成map2
                    else if (!validTypes.Contains(map1.CurrentDataType))
                    {
                        DataType = map2.CurrentDataType;
                        PortsToChangeType.UnionWith(map1.MutableTypePorts);
                    }
                    // map2需要改变类型成map1
                    else
                    {
                        DataType = map1.CurrentDataType;
                        PortsToChangeType.UnionWith(map2.MutableTypePorts);
                    }
                }
            }
        }

        bool ProcessWhenMap1IsEnum(TypeSpreadMap map1, TypeSpreadMap map2, HashSet<Type> validTypes)
        {
            // 若map1不可变，则map2必然可变。此时只有当map2可以变成任意enum时才成立
            // 而在map1可变的情况下，map1的当前类型是enum，并且AllowedTypes中有Enum
            // map2为enum的情况交给后面的处理，此处只考虑map2当前类型不是enum的情况
            // 其中，如果map2是不可变的，那么同样可以交给后面处理
            // 所以只剩下map2是可变的，此时map1和map2都是可变的，那么此处只要处理双方都是可变Enum的情况
            // 其余交给后面的流程
            if (map1.ImmutableTypePorts.Count > 0)
            {
                if (map2.AllowedTypes.Contains(typeof(Enum)))
                {
                    DataType = map1.CurrentDataType;
                    PortsToChangeType.UnionWith(map2.MutableTypePorts);
                    return true;
                }
            }
            else if (map2.ImmutableTypePorts.Count == 0)
            {
                if (validTypes.Contains(typeof(Enum)))
                {
                    DataType = map1.CurrentDataType;
                    PortsToChangeType.UnionWith(map2.MutableTypePorts);
                    return true;
                }
            }
            return false;
        }
    }
}
